-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rework messages serialization #352
Conversation
See comments in the code for details. I also switched to plain `Vec` for serialization since we no longer need `Cursor` for it and `Writer::write_all` is much slower then `Vec::extend_from_slice`.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #352 +/- ##
==========================================
+ Coverage 89.32% 89.71% +0.39%
==========================================
Files 41 44 +3
Lines 2388 2450 +62
==========================================
+ Hits 2133 2198 +65
+ Misses 255 252 -3 ☔ View full report in Codecov by Sentry. |
"Arrays" describes it better since the tick is technically also part of the header.
Check most unlikely conditions first.
Just read until the end of the cursor. Saves us a single byte.
I planning to encode other things inside it. Also this name fits better in general.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good. I think we tried a similar approach with ranges a while back but scrapped it due to perf.
- Rename `ReplicationChannel::Init` into `ReplicationChannel::Changes`. | ||
- Rename `ReplicationChannel::Update` into `ReplicationChannel::Mutations`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Rename `ReplicationChannel::Init` into `ReplicationChannel::Changes`. | |
- Rename `ReplicationChannel::Update` into `ReplicationChannel::Mutations`. | |
- Rename `ReplicationChannel::Init` into `ReplicationChannel::Update`. | |
- Rename `ReplicationChannel::Update` into `ReplicationChannel::Mutations`. |
What do you think about this instead? It would disambiguate the message type from the internal subsection called 'changes'. Changes
also seems slightly off to me for the message name.
If you agree, let's change it in a follow-up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree!
for ((message, _), client) in messages.iter_mut().zip(replicated_clients.iter_mut()) { | ||
client.remove_despawned(entity); | ||
message.write_entity(&mut shared_bytes, entity)?; | ||
message.add_despawn(entity_range.clone()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm do we need to filter out clients that don't have visibility of the despawned entity? Despawn messages could be considered an information leak.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed this as well while worked on this PR! Maybe in a follow-up since it were like this before?
if !mutate_message.is_empty() { | ||
let server_tick = write_tick_cached(&mut server_tick_range, serialized, server_tick)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if !mutate_message.is_empty() { | |
let server_tick = write_tick_cached(&mut server_tick_range, serialized, server_tick)?; | |
if !mutate_message.is_empty() { | |
debug_assert!(change_message.is_empty()); | |
let server_tick = write_tick_cached(&mut server_tick_range, serialized, server_tick)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this one was missed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this one related to the change message not consuming the whole mutate message? I.e. it's possible for mutate message to be sent with change message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok seems that way. For some reason I thought if we had a 'Changes' message then there wouldn't be a 'Mutation' message.
Co-authored-by: UkoeHB <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Applied or answered all suggestions except https://github.com/projectharmonia/bevy_replicon/pull/352/files#r1855691304, will back to it tomorrow.
- Rename `ReplicationChannel::Init` into `ReplicationChannel::Changes`. | ||
- Rename `ReplicationChannel::Update` into `ReplicationChannel::Mutations`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree!
src/client.rs
Outdated
let flags = ChangeMessageFlags::from_bits_retain(cursor.read_fixedint()?); | ||
debug_assert!(!flags.is_empty(), "message can't be empty"); | ||
|
||
let message_tick = DefaultOptions::new().deserialize_from(&mut cursor)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, will revert this part and add a comment.
Perhaps you could use one of the ChangeMessageFlags
Sounds good, but I will leave it for a follow-up.
src/client.rs
Outdated
let len = apply_dyn_array(&mut Cursor::new(&*mutate.message), |cursor| { | ||
apply_mutations(world, params, cursor, mutate.message_tick) | ||
}); | ||
|
||
match len { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to keep it as is. I usually don't write *_result
or *_option
. In Rust it's common to shadow the name if it's the same thing, but with a different type.
if let Some(mut history) = client_entity.get_mut::<ConfirmHistory>() { | ||
history.set_last_tick(message_tick); | ||
} else { | ||
commands | ||
.entity(client_entity.id()) | ||
.insert(ConfirmHistory::new(message_tick)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Already did it, but in the history rework PR 🙂 Let's keep it as is for this PR to avoid resolving conflicts.
for ((message, _), client) in messages.iter_mut().zip(replicated_clients.iter_mut()) { | ||
client.remove_despawned(entity); | ||
message.write_entity(&mut shared_bytes, entity)?; | ||
message.add_despawn(entity_range.clone()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed this as well while worked on this PR! Maybe in a follow-up since it were like this before?
Reverse the condition and remove else to reuse a few lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@UkoeHB done!
The performance is a bit slower, but we getting a free byte per changed entity and several bytes per message.
For example, in our statistic test we using only 16 bytes for a test message instead of 33.
While writing the documentation, I decided to rename
InitMessage
toChangeMessage
andUpdateMessage
toMutateMessage
(and related types). This change helps make it clear thatMutateMessage
stores only component mutations. In contrast,ChangeMessage
stores any type of change and may even include mutations if there is an insertion or removal. I considered including the rename into a separate PR, but decided to keep it here since the messages got completely reworked.I would recommend to start reading from
change_message
andmutate_message
modules, they contain a lot of internal documentation and description about how the new approach works.I also switched to plain
Vec
for serialization since we no longer needCursor
for itand
Writer::write_all
is much slower thenVec::extend_from_slice
.